package processor

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/oceanengine/ad_open_sdk_go"
	"github.com/oceanengine/ad_open_sdk_go/config"
	sdkModels "github.com/oceanengine/ad_open_sdk_go/models"
	"ocan_data_sync_task/models"
	"time"
)

// OceanCustomReportProcessor 自定义报表处理器
type OceanCustomReportProcessor struct {
	taskType models.TaskType
	sdk      *ad_open_sdk_go.Client
}

// SdkRequest SDK请求参数
type SdkRequest struct {
	AccessToken  string                                      `json:"access_token"`
	Dimensions   []string                                    `json:"dimensions"`
	AdvertiserId int64                                       `json:"advertiser_id"`
	Metrics      []string                                    `json:"metrics"`
	Filters      []*sdkModels.ReportCustomGetV30FiltersInner `json:"filters"`
	StartTime    string                                      `json:"start_time"`
	EndTime      string                                      `json:"end_time"`
	OrderBy      []*sdkModels.ReportCustomGetV30OrderByInner `json:"order_by"`
	Page         int32                                       `json:"page,omitempty"`
	PageSize     int32                                       `json:"page_size,omitempty"`
	DataTopic    sdkModels.ReportCustomGetV30DataTopic       `json:"data_topic,omitempty"`
}

// NewCustomReportProcessor 创建自定义报表处理器
func NewCustomReportProcessor() *OceanCustomReportProcessor {
	return &OceanCustomReportProcessor{
		taskType: models.TaskTypeOceanCustomReport,
		sdk:      ad_open_sdk_go.Init(config.NewConfiguration()),
	}
}

func (p *OceanCustomReportProcessor) GetType() models.TaskType {
	return p.taskType
}

// ValidateRequest 验证任务请求参数
func (p *OceanCustomReportProcessor) ValidateRequest(task *models.Task) error {

	// 验证每个任务项
	required := []string{"id", "advertiser_id"}
	for _, item := range task.Items {
		for _, field := range required {
			if _, ok := item[field]; !ok {
				return fmt.Errorf("item 缺少必填字段: %s", field)
			}
		}
	}

	// 验证必要的公共参数
	if task.CommonParams == nil {
		return fmt.Errorf("missing required common params")
	}

	// 验证SDK请求参数
	request, err := p.buildSdkRequest(task.Items[0], task.CommonParams)
	if err != nil {
		return err
	}

	if request.AccessToken == "" {
		return fmt.Errorf("公共参数缺少必填字段: access_token")
	}

	return nil
}

// Process 处理单个任务项
func (p *OceanCustomReportProcessor) Process(
	ctx context.Context,
	item map[string]interface{},
	commonParams map[string]interface{},
) (ret models.ItemTaskResult) {
	// 构建SDK请求参数
	request, err := p.buildSdkRequest(item, commonParams)

	ret.ItemID = item["id"].(string)

	if err != nil {
		ret.Error = &models.APIError{
			Code:    400,
			Message: fmt.Sprintf("构建请求参数失败: %v", err),
		}
		return ret
	}

	var allData []interface{}
	maxRetries := 3 // 最大重试次数
	retryDelay := 1 // 初始重试延迟(秒)
	for {
		select {
		case <-ctx.Done():
			ret.Error = &models.APIError{
				Code:    499,
				Message: "上下文已取消",
			}
			return ret
		default:
		}

		// 调用API获取数据，带重试机制
		var data []interface{}
		var hasMore bool
		var apiErr *models.APIError

		for retry := 0; retry < maxRetries; retry++ {
			data, hasMore, apiErr = p.fetchReportData(ctx, request)

			// 成功
			if apiErr == nil {
				break
			}

			// 特殊失败错误，直接跳出重试
			codeMap := map[int]bool{
				40002: true,  // 没有权限
				40102: true,  // 传入Access_Token已失效，请重新获取
				40100: true,  // 请求超过服务整体频控，可以在开发者频控范围内重新尝试请求
				40110: true,  // 请求超过开发者频控范围，请登陆开发者后台-接口频控处查看当前接口对应分配qps，在要求范围内请求接口
				50000: false, // 请求失败 可重新请求
			}
			if codeMap[apiErr.Code] {
				// 中断
				ret.IsBreak = true
				break
			}

			// 最后一次重试失败
			if retry == maxRetries-1 {
				ret.Error = apiErr
				return ret
			}

			// 等待后重试，使用指数退避
			select {
			case <-ctx.Done():
				ret.Error = &models.APIError{
					Code:    499,
					Message: "上下文已取消",
				}
				return ret
			case <-time.After(time.Duration(retryDelay) * time.Second):
				retryDelay *= 2
			}
		}

		// 处理非请求失败的错误
		if apiErr != nil {
			ret.Error = apiErr
			return ret
		}

		// 追加数据
		allData = append(allData, data...)

		// 判断是否继续获取下一页
		if !hasMore {
			break
		}
		request.Page++
	}

	ret.Data = allData
	return ret
}

// buildSdkRequest 构建SDK请求参数
func (p *OceanCustomReportProcessor) buildSdkRequest(
	item map[string]interface{},
	commonParams map[string]interface{},
) (*SdkRequest, error) {
	// 合并参数
	mergedParams := make(map[string]interface{})
	for k, v := range commonParams {
		mergedParams[k] = v
	}
	for k, v := range item {
		mergedParams[k] = v
	}

	// 转换为SDK请求参数
	var request SdkRequest
	paramsBytes, err := json.Marshal(mergedParams)
	if err != nil {
		return nil, fmt.Errorf("marshal params failed: %v", err)
	}

	if err := json.Unmarshal(paramsBytes, &request); err != nil {
		return nil, fmt.Errorf("unmarshal to sdk request failed: %v", err)
	}

	// 设置默认值
	if request.Page == 0 {
		request.Page = 1
	}
	if request.PageSize == 0 {
		request.PageSize = 100
	}

	return &request, nil
}

// fetchReportData 获取报表数据
func (p *OceanCustomReportProcessor) fetchReportData(
	ctx context.Context,
	request *SdkRequest,
) ([]interface{}, bool, *models.APIError) {
	// 调用SDK
	resp, httpRes, err := p.sdk.ReportCustomGetV30Api().
		Get(ctx).
		AccessToken(request.AccessToken).
		Dimensions(request.Dimensions).
		AdvertiserId(request.AdvertiserId).
		Metrics(request.Metrics).
		Filters(request.Filters).
		StartTime(request.StartTime).
		EndTime(request.EndTime).
		OrderBy(request.OrderBy).
		Page(request.Page).
		PageSize(request.PageSize).
		DataTopic(request.DataTopic).
		Execute()

	// 网络请求错误
	if err != nil {
		msg := err.Error()
		if httpRes != nil {
			msg += fmt.Sprintf(", http status: %s", httpRes.Status)
		}
		return nil, false, &models.APIError{
			Code:    500,
			Message: msg,
		}
	}

	// 检查响应状态
	if resp == nil || resp.Code == nil {
		return nil, false, &models.APIError{
			Code:    500,
			Message: "invalid response",
		}
	}

	// 处理业务错误
	if *resp.Code != 0 {
		return nil, false, &models.APIError{
			Code:    int(*resp.Code),
			Message: *resp.Message,
		}
	}

	// 提取数据和分页信息
	var data []interface{}
	if resp.Data != nil && resp.Data.Rows != nil {
		for _, row := range resp.Data.Rows {
			data = append(data, row)
		}
	}

	hasMore := false
	if resp.Data != nil && resp.Data.PageInfo != nil {
		hasMore = *resp.Data.PageInfo.Page < *resp.Data.PageInfo.TotalPage
	}

	return data, hasMore, nil
}
